package net.cyndeline.scalarlib.rldungeon.dgs.strategy.pointlessArea.help

import net.cyndeline.scalarlib.rldungeon.dgs.graph.{PointlessAreaData, DGSFunctionality, Room}
import scalax.collection.GraphEdge.UnDiEdge
import scalax.collection.GraphPredef.OuterEdge
import scalax.collection.immutable.Graph
import net.cyndeline.scalarlib.rldungeon.dgs.strategy.help.{SuperGraphVertexReplacer, CollapsedNode, CollapsedEdge}
import net.cyndeline.scalarlib.rldungeon.dgs.strategy.pointlessArea.algorithms.help.{RoomModifier, RoomModifierInterface, PointlessAreaResult}
import net.cyndeline.scalarlib.rlgraph.util.{EdgeCopyFactory, VertexReplacer}
import com.escalatesoft.subcut.inject.{BindingModule, Injectable}

/**
 * Used for injection.
 */
trait PointlessAreaDataSetterI {
  def addPointlessAreaDataAndStatus[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
                                   (graph: Graph[VType, EType],
                                    collapsedGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner],
                                    pointlessAreas: Vector[PointlessAreaResult[VType]],
                                    edgeFactory: EdgeCopyFactory[VType, EType]
                                    ): PointlessSetterResult[VType, EType]
}

/**
 * Creates data storage objects for pointless areas and adds the object to every room in the area.
 *
 * @constructor Creates a new pointlerr-area data setter.
 */
class PointlessAreaDataSetter(implicit val bindingModule: BindingModule)
  extends PointlessAreaDataSetterI
  with Injectable {

  private val roomModifier = injectOptional[RoomModifierInterface] getOrElse { new RoomModifier() }

  /**
   * @param graph A graph representing an entire level.
   * @param collapsedGraph A cycle-collapsed version of the original graph.
   * @param pointlessAreas Areas with references to the rooms in the original graph and the rooms represented in
   *                       the collapsed graph.
   * @return An updated tuple of the original and collapsed graph, with pointless rooms marked as such and with their
   *         pointless area data set.
   */
  override def addPointlessAreaDataAndStatus[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
                                   (graph: Graph[VType, EType],
                                    collapsedGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner],
                                    pointlessAreas: Vector[PointlessAreaResult[VType]],
                                    edgeFactory: EdgeCopyFactory[VType, EType]
                                    ): PointlessSetterResult[VType, EType] = {
    val vertexReplacer = new VertexReplacer[VType, EType](edgeFactory)
    val superGraphVertexReplacer = new SuperGraphVertexReplacer()
    var areaId = 0
    var currentGraph = graph
    var currentCollapsedGraph = collapsedGraph

    var oldToNewVertices = Map[VType, VType]()
    var oldToNewNodes = Map[CollapsedNode[VType], CollapsedNode[VType]]()

    val allPointlessAreas = pointlessAreas.iterator
    while (allPointlessAreas.hasNext) {
      val area = allPointlessAreas.next()
      val areaData = PointlessAreaData[VType](areaId, area.mainPathConnection)

      val pointlessRooms = roomModifier.setPointlessStatus(true, area.pointlessRooms)
      val roomsWithData = roomModifier.addPointlessAreaData(areaData, pointlessRooms.values.toSet)

      /* Map the keys in the pointless-room map to the values in the data-map since they keys are still found
       * in the graphs, but the data-values is what they should be replaced with.
       */
      val updatedRoomMap: Map[VType, VType] = (for (room <- pointlessRooms.keys) yield room -> roomsWithData(pointlessRooms(room))).toMap
      oldToNewVertices ++= updatedRoomMap

      val oldToNewRooms = updatedRoomMap.iterator
      while (oldToNewRooms.hasNext) {
        val oldToNewRoom = oldToNewRooms.next()
        currentGraph = vertexReplacer.replace(oldToNewRoom._1, oldToNewRoom._2, currentGraph)

        val replacedNodeData: (Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner], CollapsedNode[VType], CollapsedNode[VType]) =
          superGraphVertexReplacer.replaceVertex(oldToNewRoom._1, oldToNewRoom._2, currentCollapsedGraph)
        currentCollapsedGraph = replacedNodeData._1
        val newNode = replacedNodeData._2
        val oldNode = replacedNodeData._3

        oldToNewNodes += (oldNode -> newNode)
      }

      areaId += 1
    }

    PointlessSetterResult(currentGraph, currentCollapsedGraph, oldToNewVertices, oldToNewNodes)
  }
}
